import { AssignStmt, Constant, InvokeExpr, InvokeStmt, Variable, FieldReference, MethodDefStmt } from "qvog-types";
import { pattern } from '~/core/dsl/fluent/QueryDescriptor';
import { DataFlow, DfgTraverse, HamiltonFlow, TaintFlow } from "~/lib/flow";

const PWD_ARRAY = ["password", "pwd", "pw", "passwd", "pass", "passw", "pswd"];
const sensitiveFields = [
    // 用户身份信息
    "email",
    "emailAddress",
    "fullName",
    "phoneNumber",
    "socialSecurityNumber",
    "idNumber",
    "passportNumber",
    "dateOfBirth",

    // 认证凭证
    "password",
    "token",
    "apiKey",
    "secretKey",
    "accessToken",
    "refreshToken",
    "sessionId",
    "userId",

    // 财务信息
    "creditCardNumber",
    "bankAccountNumber",
    "transactionId",
    "transactionAmount",
    "billingAddress",
    "paymentMethod",
    "cardNumber",
    "cvv",

    // 位置信息
    "homeAddress",
    "gpsCoordinates",
    "currentLocation",
    "ipAddress",

    // 健康信息
    "medicalRecordNumber",
    "healthCondition",
    "prescriptionDetails",
    "allergies",
    "bloodType",

    // 其他敏感字段
    "motherMaidenName",
    "securityQuestion",
    "securityAnswer",
    "deviceFingerprint",
    "internalState",
    "debugKey",
    "encryptionKey"
];

export const FindLocalStoragePasswordThroughDataFlow2 = pattern(
    "Find LocalStorage Password Data Flow", q => q
        .from(f => f.withData(v => v.stream().any(s =>
            s instanceof Variable && PWD_ARRAY.includes(s.name)
        )).as("Source"))
        .from(f => f.withPredicate(s => {
            if (s instanceof InvokeStmt) {
                let expr: InvokeExpr = s.expression;
                return expr.target === "setItem" && (base => base instanceof Variable ? base.name === 'localStorage' || base.name === 'sessionStorage' : false)(expr.base);
            }
            return false;
        }).as("Sink"))
        .exists((f) => f.configure({
            flow: new HamiltonFlow(),
            strategy: new DfgTraverse()
        }).source("Source").sink("Sink").as("DataFlow"), () => new DataFlow())
        .select("Source", "Sink", "DataFlow")
);

export const FindLogSensitiveInfo2 = pattern(
    "Find Log Sensitive Info", q => q
        .from(f => f
            .withData(v => v.stream().any(s =>
                s instanceof Variable && sensitiveFields.includes(s.name)
            )).as("Source"))
        .from(f => f.withPredicate(v => v.stream().any(s =>
            s instanceof InvokeExpr && s.target === "log" && (base => base instanceof Variable ? base.name == "console" : false)(s.base))).as("Sink"))
        .exists(f => f.configure({
            flow: new HamiltonFlow(),
            strategy: new DfgTraverse()
        }).source("Source").sink("Sink").as("DataFlow"), () => new DataFlow())
        .select("Source", "Sink", "DataFlow")
);

export const FindXSS2 = pattern(
    "Find XSS", q => q
        .from(f => f.withData(v => v.stream().any(s =>
            s instanceof Constant && s.value.match(/<script>.*<\/script>/) !== null
        )).as("Source"))
        .from(f => f.withPredicate(s => s instanceof AssignStmt &&
            (target => target instanceof FieldReference && target.name === "innerHTML")(s.target)).as("Sink"))
        .from(f => f.withPredicate(v => v.stream().any(s =>
            s instanceof InvokeExpr && s.target === "sanitize")).as("Barrier"))
        .exists(f => f.source("Source").barrier("Barrier").sink("Sink").as("TaintFlow"), () => new TaintFlow())
        .select("Source", "Sink", "TaintFlow")
);

export const FindExcessiveSQLPrivileges2 = pattern(
    "Find Excessive SQL Privileges", q => q
        .from(f => f.withData(v => v.stream().any(s =>
            s instanceof Constant &&
            s.type.name === "string" &&
            /GRANT\s+ALL\s+PRIVILEGES/i.test(s.value) &&
            /ON\s+.*\*.*\s+TO/i.test(s.value)
        )).as("Source"))

        .from(f => f.withPredicate(s => {
            if (!(s instanceof InvokeStmt)) return false;

            const expr = s.expression as InvokeExpr;
            const target = expr.target;
            return ["execute", "query", "run"].includes(target);
        }).as("Sink"))

        .exists(f => f.configure({
            flow: new HamiltonFlow(),
            strategy: new DfgTraverse()
        }).source("Source").sink("Sink").as("DataFlow"), () => new DataFlow())
        .select("Source", "Sink", "DataFlow")
);


export const FindUnsafeFilePath2 = pattern(
    "Find Unsafe File Path", q => q
        .from(f => f.withData(v => v.stream().any(s =>
            s instanceof InvokeExpr && ["onClick", "onChange"].includes(s.target)
        )).as("Source"))

        .from(f => f.withPredicate(s => {
            if (!(s instanceof InvokeStmt)) return false;

            const expr = s.expression as InvokeExpr;
            const target = expr.target;
            const isFileOperate = ["openSync", "writeSync", "readSync", "open", "write", "read"].includes(target);

            return isFileOperate;
        }).as("Sink"))

        .exists(f => f.configure({
            flow: new HamiltonFlow(),
            strategy: new DfgTraverse()
        }).source("Source").sink("Sink").as("DataFlow"), () => new DataFlow())
        .select("Source", "Sink", "DataFlow")
);

export const FindJsonParseProto = pattern(
    "Find Json Parse Proto", q => q
        .from(f => f.withData(v => v.stream().any(s =>
            s instanceof Constant && s.value.match(/__proto__/) !== null
        )).as("Source"))
        .from(f => f.withData(v => v.stream().any(s =>
            s instanceof InvokeExpr && s.target === "parse" &&
            (base => base instanceof Variable ? base.name === 'JSON' : false)(s.base)
        )).as("Sink"))
        .exists(f => f.configure({
            flow: new HamiltonFlow(),
            strategy: new DfgTraverse()
        }).source("Source").sink("Sink").as("DataFlow"), () => new DataFlow())
        .select("Source", "Sink", "DataFlow")
)

export const FindUnsafeRouter = pattern(
    "Find Unsafe Router", q => q
        .from(f => f.withData(v => v.stream().any(s =>
            s instanceof InvokeExpr && ["onClick", "onChange"].includes(s.target)
        )).as("Source"))
        .from(f => f.withData(v => v.stream().any(s =>
            s instanceof InvokeExpr && s.target === "pushUrl" &&
            (base => base instanceof Variable ? base.name === 'router' : false)(s.base)
        )).as("Sink"))
        .exists(f => f.configure({
            flow: new HamiltonFlow(),
            strategy: new DfgTraverse()
        }).source("Source").sink("Sink").as("DataFlow"), () => new DataFlow())
        .select("Source", "Sink", "DataFlow")
);

export const FindEval = pattern(
    "Find Eval", q => q
        .from(f => f.withData(v => v.stream().any(s =>
            s instanceof InvokeExpr && s.target === "eval"
        )).as("Warning"))
        .select("Warning")
);

export const FindUnsafeSql = pattern(
    "Find Unsafe Router", q => q
        .from(f => f.withData(v => v.stream().any(s =>
            s instanceof InvokeExpr && ["onClick", "onChange"].includes(s.target)
        )).as("Source"))
        .from(f => f.withData(v => v.stream().any(s =>
            s instanceof Constant && s.value.match(/SELECT.*FROM/i) !== null
        )).as("Sink"))
        .exists(f => f.configure({
            flow: new HamiltonFlow(),
            strategy: new DfgTraverse()
        }).source("Source").sink("Sink").as("DataFlow"), () => new DataFlow())
        .select("Source", "Sink", "DataFlow")
);

export const FindCallingDeprecatedDecorators = pattern(
    "Find Calling Deprecated Decorators", q => q
        .from(f => f.withData(v => v.stream().any(s =>
            s instanceof InvokeExpr
        )).as("Source"))
        .from(f => f.withData(v => v.stream().any(s =>
            s instanceof MethodDefStmt && s.decorators.some(decorator => decorator === "@deprecated")
        )).as("Sink"))
        .exists(f => f.configure({
            flow: new HamiltonFlow(),
            strategy: new DfgTraverse()
        }).source("Source").sink("Sink").as("DataFlow"), () => new DataFlow())
        .select("Source", "Sink", "DataFlow")
);
